#ifndef MY_MATCHER_HPP
#define MY_MATCHER_HPP

#include "util.hpp"
#include "db.hpp"
#include "online.hpp"
#include "room.hpp"
#include <list>
#include <mutex>
#include <condition_variable>
#include <thread>

//匹配管理模块

//匹配（阻塞）队列
template<class T>
class match_queue
{
private:
    //用链表而不是queue是因为我们有中间删除数据的需要
    std::list<T> _list;
    //实现线程安全
    std::mutex _mutex;
    //这个条件变量主要为了阻塞消费者，后面使用的时候，队列中元素个数<2就阻塞
    std::condition_variable _cond;
public:

    //获取元素个数
    int size()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.size();        
    }

    //判断是否为空
    bool empty()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.empty();
    }

    //阻塞线程
    void wait()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _cond.wait(lock);
    }

    //入队数据，并唤醒线程
    void push(const T& data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.push_back(data);
        _cond.notify_all();
    }

    //出队数据
    bool pop(T& data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if(_list.empty())
        {
            return false;
        }
        data = _list.front();
        _list.pop_front();
        return true;
    }

    //移除指定的数据
    void remove(T& data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.remove(data);
    }
};

//匹配管理
class matcher
{
private:
    //普通选手匹配队列
    match_queue<uint64_t> _q_normal;
    //高手匹配队列
    match_queue<uint64_t> _q_master;
    //大神匹配队列
    match_queue<uint64_t> _q_expert;
    //对应三个匹配队列的处理线程
    std::thread _th_normal;
    std::thread _th_master;
    std::thread _th_expert;

    //房间管理句柄
    room_manager* _rm;
    //用户数据句柄
    user_table* _ut;
    //在线用户句柄
    online_manager* _om;

private:
    //总的处理函数
    void handle_match(match_queue<uint64_t>& mq)
    {
        while(1)
        {
            //1.判断队列人数是否大于2，<2就阻塞等待
            while(mq.size() < 2)
            {
                mq.wait();
            }
            //2.走下来说明人数够了，出队两个玩家
            uint64_t uid1,uid2;
            bool ret = mq.pop(uid1);
            if(ret == false)
            {
                continue;
            }
            ret = mq.pop(uid2);
            if(ret == false)
            {
                mq.push(uid1);
                continue;
            }
            //3.检验两个玩家是否在线，如果有人掉线，则把另一个玩家重新添加到队列
            wssrv_t::connection_ptr conn1 = _om->get_conn_from_hall(uid1);
            if(conn1.get() == nullptr)
            {
                mq.push(uid2);
                continue;
            }
            wssrv_t::connection_ptr conn2 = _om->get_conn_from_hall(uid2);
            if(conn2.get() == nullptr)
            {
                mq.push(uid1);
                continue;
            }
            //4.为两个玩家创建房间，并且将玩家加入房间
            room_ptr rp = _rm->create_room(uid1, uid2);
            if(rp.get() == nullptr)
            {
                mq.push(uid1);
                mq.push(uid2);
                continue;
            }
            //5.对两个玩家进行响应
            Json::Value resp;
            resp["optype"] = "match_success";
            resp["result"] = "true";
            std::string body;
            json_util::serialize(resp, body);
            conn1->send(body);
            conn2->send(body);
        }
       
    }

    //分别是三个线程的执行函数即入口
    void th_normal_entry()
    {
        handle_match(_q_normal);
    }
    void th_master_entry()
    {
        handle_match(_q_master);
    }
    void th_expert_entry()
    {
        handle_match(_q_expert);
    }

public:
    matcher(room_manager* rm, user_table* ut, online_manager* om)
        : _rm(rm), _ut(ut), _om(om),
          _th_normal(std::thread(&matcher::th_normal_entry, this)),
          _th_master(std::thread(&matcher::th_master_entry, this)),
          _th_expert(std::thread(&matcher::th_expert_entry, this))
    {
        DLOG("游戏匹配模块初始化完毕");
    }

    //添加用户到匹配队列
    bool add(uint64_t uid)
    {
        //首先通过用户ID获取用户信息
        Json::Value user;
        bool ret = _ut->select_by_id(uid, user);
        if(ret == false)
        {
            DLOG("找不到ID为%lu的用户信息", uid);
            return false;
        }
        //获取分数，根据分数决定去哪一个匹配队列
        int score = user["score"].asInt();
        if(score < 2000)
        {
            _q_normal.push(uid);
        }
        else if(score >= 2000 && score < 3000)
        {
            _q_master.push(uid);
        }
        else
        {
            _q_master.push(uid);
        }
        return true;
    }

    //从匹配队列移除用户
    bool del(uint64_t uid)
    {
        //首先通过用户ID获取用户信息
        Json::Value user;
        bool ret = _ut->select_by_id(uid, user);
        if(ret == false)
        {
            DLOG("找不到ID为%lu的用户信息", uid);
            return false;
        }
        //获取分数，根据分数决定去哪一个匹配队列
        int score = user["score"].asInt();
        if(score < 2000)
        {
            _q_normal.remove(uid);
        }
        else if(score >= 2000 && score < 3000)
        {
            _q_master.remove(uid);
        }
        else
        {
            _q_master.remove(uid);
        }
        return true;
    }
};

#endif
